<template>
    <div style="padding: 2%;">
        <a-card>
            <!-- 顶栏 -->
            <template #title>
                <div style="display: flex;align-items:center;">
                    <span style="font: 700 16px 微软雅黑">文件管理</span>

                    <div style="flex-grow: 1;" />

                </div>

            </template>

            <a-card style="width: 90%;">

                <template #title>
                    <div style="display: flex;align-items:center;">
                        <span style="font: 700 16px 微软雅黑">目录</span>

                        <div style="flex-grow: 1;" />

                    </div>
                </template>

                <!-- 目录树 -->
                <a-tree v-model:expandedKeys.lazy="expandedKeys" v-model:selectedKeys="selectedKeys"
                    @select="selectNode" :tree-data="treeData" show-line :height="getViewHeight(0.6)">

                    <!-- 树节点图标 -->
                    <template #switcherIcon="{ switcherCls }"><down-outlined :class="switcherCls" /></template>

                    <template #title="{ title, key, isLeaf }">

                        <a-dropdown :trigger="['contextmenu']">
                            <div style="width: 60vw;position: relative;">
                                <span style="display: inline-block;">{{ title }}</span>

                                <a-space style="position: absolute;right: 0;">

                                    <a-space v-if="isLeaf">
                                        <a :href="'/fileApi/download?fileName=' + key" target="_blank" v-if="isImg(key)"
                                            @click.stop="">预览</a>

                                        <a :href="'/fileApi/download?fileName=' + key" download @click.stop="">下载</a>
                                    </a-space>

                                </a-space>

                            </div>

                            <!-- 右键菜单 -->
                            <template #overlay>
                                <a-menu>
                                    <a-menu-item v-if="isLeaf">
                                        <a :href="'/fileApi/download?fileName=' + key" download>下载</a>
                                    </a-menu-item>
                                    <a-menu-item v-else @click="() => { selectedKeys[0] = key; isInsert = true; }">新增
                                    </a-menu-item>

                                    <a-menu-item @click="usePrompt('不可逆操作，请谨慎执行', deleteFile, key)">
                                        <span>删除</span>
                                    </a-menu-item>

                                </a-menu>
                            </template>
                        </a-dropdown>

                    </template>

                </a-tree>
            </a-card>


        </a-card>
    </div>

    <!-- 新增弹框 -->
    <a-modal v-model:open="isInsert" title="新增" :keyboard="false" :maskClosable="false">
        <a-form :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">

            <a-form-item label="当前路径">
                <a-input :value="selectedKeys[0]" disabled />
            </a-form-item>

            <a-form-item label="上传文件">

                <a-upload :class="{ 'dp-i-b': fileList?.length === 0, 'bp-b': fileList?.length === 1 }"
                    v-model:file-list="fileList" :max-count="1" list-type="picture" :before-upload="() => false">
                    <a-button>
                        <upload-outlined></upload-outlined>
                        选择文件
                    </a-button>
                </a-upload>

            </a-form-item>

            <a-form-item label="文件名称" v-if="fileList?.length === 1">

                <a-input-group compact>
                    <a-tooltip placement="top" title="服务端将以此名称存储文件">
                        <a-input v-model:value="fileName" :placeholder="fileList[0].name"
                            style="width: calc(100% - 100px)" />
                    </a-tooltip>
                    <a-button type="primary" @click="uploadFile" :loading="loading">上传文件</a-button>
                </a-input-group>

            </a-form-item>

            <a-form-item label="文件夹">

                <a-input-group compact>
                    <a-input v-model:value="folderName" style="width: calc(100% - 100px)" />
                    <a-button style="width: 100px;" type="primary" @click="mkdirs" :loading="loading">新建文件夹</a-button>
                </a-input-group>

            </a-form-item>


        </a-form>
        <template #footer>
            <a-button @click="isInsert = false">关闭</a-button>
        </template>
    </a-modal>

    <!-- 提示弹窗 -->
    <a-modal v-model:open="promptVisible" title="提示" @Ok="promptOk()" @cancel="promptVisible = false" :width="300"
        okType="default" :cancelButtonProps="{
            type: 'primary'
        }">
        {{ promptText }}
    </a-modal>


</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { message, type TreeProps } from 'ant-design-vue';
import { HttpStatusCode } from 'axios'
import type { UploadProps } from 'ant-design-vue';
import { fileApi } from '@/myAxios';

// 文件最大尺寸，MB
// 10MB
const countMB = 10;
const MaxFileSize = countMB * 1024 * 1024;

const imgExt = [
    'jpg',
    'png',
    'gif',
    'bmp',
    'webp',
]
const fileExt = [
    ...imgExt,
    'epub',
    'txt',
    'zip',
    'pdf',
    'exe',
]

const expandedKeys = ref<string[]>([]);
const selectedKeys = ref<string[]>([]);
const treeData = ref<TreeProps[]>([]);


const loading = ref(false);

const isInsert = ref(false);
const folderName = ref<string>('')

const fileList = ref<UploadProps['fileList']>([]);
const fileName = ref<string>('')

const promptVisible = ref(false);
const promptText = ref('')
const promptOk = ref<Function>(() => { });

onMounted(
    () => {
        getFileList()
    }
)

// 获取文件数据
const getFileList = async () => {
    try {
        const result = await fileApi.get('/list?filePath=')
        if (result.status == 200) {
            const fileListTree: TreeProps['treeData'] = [];
            result.data.forEach((e: string) => {
                fileListTree.push({
                    title: e,
                    key: e,
                    isLeaf: false,
                })
            });
            console.log(fileListTree);
            treeData.value = fileListTree
        }
    } catch (error) {
        console.log(error);
    }
}
// 加载子节点
const onLoadData = async (treeNode: any) => {
    // 剥离出原始数据
    treeNode = treeNode.dataRef;
    // console.log('loading key: ' + treeNode.key);

    try {
        const result = await fileApi.get('/list?filePath=' + treeNode.key)
        if (result.status === HttpStatusCode.Ok) {

            // 空文件夹提示
            if (result.data.length === 0) {
                clearChildren(treeNode);
                message.warn('文件夹 \'' + treeNode.key + '\' 为空');
                return;
            }

            // 目录树写入
            const fileListTree: TreeProps['treeData'] = [];
            result.data.forEach((e: string) => {
                let node = {
                    title: e,
                    key: treeNode.key + "/" + e,
                    isLeaf: false,
                };
                const ext = e.substring(e.lastIndexOf('.') + 1);
                if (fileExt.includes(ext)) {
                    node.isLeaf = true;
                }
                fileListTree.push(node);
            });

            treeNode.children = fileListTree
            // 自动展开加载的节点
            expandByKey(treeNode.key)
        }

    } catch (error) {
        console.log(error);
    }

}

// 操作文件
const uploadFile = async () => {
    loading.value = true;
    try {
        const formData = new FormData();

        if (fileList.value && fileList.value.length > 0) {
            const file = fileList.value[0].originFileObj as File;
            if (file.size > MaxFileSize) {
                message.warn('文件大小超出 ' + countMB + 'MB，请重新选择文件！！！');
                return;
            }
            formData.append('file', file);
            if (fileName.value === '') fileName.value = file.name;
            formData.append('path', selectedKeys.value[0] + "/" + fileName.value);
        } else {
            message.error("文件列表为空");
            return
        }

        const result = await fileApi.post('/upload', formData);

        if (result.data.code === HttpStatusCode.Ok) {
            onLoadDataByKey(selectedKeys.value[0])
            message.success('文件上传成功！！！');
            isInsert.value = false;
            fileList.value = [];
            fileName.value = ''
        } else if (result.data.code === HttpStatusCode.PayloadTooLarge) {
            message.warn('文件大小超出服务端限制');
        } else {
            console.log(result.data);
            message.error('文件上传失败！！！');
        }

    } catch (err) {
        console.log(err);
        message.error('文件上传失败！！！');
    } finally {
        loading.value = false;
    }
}
const mkdirs = async () => {
    loading.value = true;
    try {
        const path = selectedKeys.value[0] + "/" + folderName.value;
        const result = await fileApi.get('/mkdirs?path=' + path)
        if (result.data.code === HttpStatusCode.Ok) {
            // 重新加载，父节点的数据
            onLoadDataByKey(selectedKeys.value[0])

            message.success('新建文件夹 \'' + folderName.value + '\' 成功');
        } else if (result.data.code === HttpStatusCode.Conflict) {
            message.warn(result.data.msg);
        }
    } catch (error) {
        console.log(error);
    } finally {
        loading.value = false;
    }
}
const deleteFile = async (key: string) => {
    loading.value = true;
    try {
        const result = await fileApi.delete('/delete?path=' + key)
        // 请求成功，修改视图状态
        if (result.data.code === HttpStatusCode.Ok) {

            // 收起节点
            if (expandedKeys.value.includes(key)) {
                expandedKeys.value = expandedKeys.value.filter(e => e !== key);
            }

            // 获取父节点key
            const arr = key.split('/')
            arr.pop()
            const parentKey = arr.join('/');

            // 选择指向切换为父节点
            selectedKeys.value[0] = parentKey;

            // 重新加载父节点
            onLoadDataByKey(parentKey);

            // 消息提示
            message.success('删除成功');
        } else {
            message.error('删除 \'' + key + '\' 失败');
            console.log(result.data.msg);
        }
    } catch (error) {
        console.log(error);
    } finally {
        loading.value = false;
    }
}

// 辅助函数，效果函数
const selectNode = async (_selectedKeys: string[], info: any) => {
    // console.log(info.node);

    if (!info.node.isLeaf) {
        if (!expandedKeys.value?.includes(info.node.key)) {
            expandedKeys.value?.push(info.node.key);
            if (!info.node.children) {
                onLoadData(info.node);
            }
        }
        else {
            collapseByKey(info.node.key)
        }
    }
}
const isImg = (fileName: string) => {
    const ext = fileName.substring(fileName.lastIndexOf('.') + 1);
    if (imgExt.includes(ext)) {
        return true;
    }
    return false;
}
const getViewHeight = (rate: number) => {
    return window.innerHeight * rate;
}

const onLoadDataByKey = (key: string) => {
    const node = findNodeByKey(key);
    const treeNode = {
        dataRef: node
    }
    onLoadData(treeNode);
}
const findNodeByKey = (targetKey: string) => {
    // 分割目标key，以获取层级信息
    const path = targetKey.split('/');
    return searchTree(treeData.value, path, 0);
}
const searchTree = (nodeList: any[], path: string[], index: number) => {
    // 如果当前层级与path的当前部分匹配，继续深入
    let key = '';
    for (let i = 0; i <= index; i++) {
        if (i !== 0) {
            key += '/';
        }
        key += path[i];
    }

    for (let i = 0; i < nodeList.length; i++) {
        const node = nodeList[i];
        if (node.key === key) {
            // 如果我们到达了path的末尾，说明找到了节点
            if (index + 1 === path.length) {
                return node;
            }
            if (node.children.length === 0) {
                console.log('无子节点，目标key有误？');
                return null;
            }

            const r = searchTree(node.children, path, index + 1) as any;
            return r;
        }
    }

    return null;
}
const clearChildren = (treeNode: any) => {
    if (treeNode.children === undefined) return

    treeNode.children.forEach((e: any) => {
        clearChildren(e);
    });
    treeNode.children = undefined;
}
const collapseByKey = (key: string) => {
    // 折叠节点
    expandedKeys.value = expandedKeys.value.filter(e => e !== key);
}
const expandByKey = (key: string) => {
    // 展开节点
    if (!expandedKeys.value?.includes(key)) {
        expandedKeys.value?.push(key);
    }
}
const usePrompt = (message: string, OkFunc: Function, ...args: any[]) => {
    promptVisible.value = true;
    promptText.value = message;
    promptOk.value = () => {
        OkFunc(...args);
        promptVisible.value = false;
    }
}



</script>

<style lang="css">
.dp-i-b {
    display: inline-block;
}

.dp-b {
    display: block;
}

.m-t-10 {
    margin-top: 10px;
}
</style>